In [ ]:
# Let printing work the same in Python 2 and 3
from __future__ import print_function
# Turning on inline plots -- just for use in ipython notebooks.
import matplotlib
matplotlib.use('nbagg')
import numpy as np
import matplotlib.pyplot as plt
In the previous parts, you learned how matplotlib organizes plot-making by figures and axes. We broke down the components of a basic figure and learned how to create them. You also learned how to add one or more axes to a figure, and how to tie them together. You even learned how to change some of the basic appearances of the axes. Finally, we went over some of the many plotting methods that matplotlib has to draw on those axes. With all that knowledge, you should be off making great and wonderful figures. Why are you still here?
"We don't know how to control our plots and figures!" says some random voice in the back of the room.
Of course! While the previous sections may have taught you some of the structure and syntax of matplotlib, it did not describe much of the substance and vocabulary of the library. This section will go over many of the properties that are used throughout the library. Note that while many of the examples in this section may show one way of setting a particular property, that property may be applicible elsewhere in completely different context. This is the "language" of matplotlib.
This is, perhaps, the most important piece of vocabulary in matplotlib. Given that matplotlib is a plotting library, colors are associated with everything that is plotted in your figures. Matplotlib supports a very robust language for specifying colors that should be familiar to a wide variety of users.
First, colors can be given as strings. For very basic colors, you can even get away with just a single letter:
Other colornames that are allowed are the HTML/CSS colornames such as "burlywood" and "chartreuse" are valid. See the full list of the 147 colornames. For the British speaking and poor spellers among us (Note, I am not implying that British speakers are poor spellers!), we allow "grey" where-ever "gray" appears in that list of colornames. All of these colornames are case-insensitive.
Colors can also be specified by supplying an HTML/CSS hex string, such as '#0000FF'
for blue.
A gray level can be given instead of a color by passing a string representation of a number between 0 and 1, inclusive. '0.0'
is black, while '1.0'
is white. '0.75'
would be a lighter shade of gray.
You may come upon instances where the previous ways of specifying colors do not work. This can sometimes happen in some of the deeper, stranger levels of the code. When all else fails, the universal language of colors for matplotlib is the RGB[A] tuple. This is the "Red", "Green", "Blue", and sometimes "Alpha" tuple of floats in the range of [0, 1]. One means full saturation of that channel, so a red RGBA tuple would be (1.0, 0.0, 0.0, 1.0)
, whereas a partly transparent green RGBA tuple would be (0.0, 1.0, 0.0, 0.75)
. The documentation will usually specify whether it accepts RGB or RGBA tuples. Sometimes, a list of tuples would be required for multiple colors, and you can even supply a Nx3 or Nx4 numpy array in such cases.
In functions such as plot()
and scatter()
, while it may appear that they can take a color specification, what they really need is a "format specification", which includes color as part of the format. Unfortunately, such specifications are string only and so RGB[A] tuples are not supported for such arguments (but you can still pass an RGB[A] tuple for a "color" argument).
Note, oftentimes there is a separate argument for "alpha" where-ever you can specify a color. The value for "alpha" will usually take precedence over the alpha value in the RGBA tuple. There is no easy way around this problem.
In [ ]:
%load exercises/3.1-colors.py
In [ ]:
t = np.arange(0.0, 5.0, 0.2)
plt.plot(t, t, , t, t**2, , t, t**3, )
plt.show()
In [ ]:
t = np.arange(0.0, 5.0, 0.2)
plt.plot(t, t, , t, t**2, , t, t**3, )
plt.show()
Markers are commonly used in plot()
and scatter()
plots, but also show up elsewhere. There is a wide set of markers available, and custom markers can even be specified.
marker | description | marker | description | marker | description | marker | description | |||
---|---|---|---|---|---|---|---|---|---|---|
"." | point | "+" | plus | "," | pixel | "x" | cross | |||
"o" | circle | "D" | diamond | "d" | thin_diamond | |||||
"8" | octagon | "s" | square | "p" | pentagon | "*" | star | |||
"|" | vertical line | "_" | horizontal line | "h" | hexagon1 | "H" | hexagon2 | |||
0 | tickleft | 4 | caretleft | "<" | triangle_left | "3" | tri_left | |||
1 | tickright | 5 | caretright | ">" | triangle_right | "4" | tri_right | |||
2 | tickup | 6 | caretup | "^" | triangle_up | "2" | tri_up | |||
3 | tickdown | 7 | caretdown | "v" | triangle_down | "1" | tri_down | |||
"None" | nothing | None |
nothing | " " | nothing | "" | nothing |
In [ ]:
xs, ys = np.mgrid[:4, 9:0:-1]
markers = [".", "+", ",", "x", "o", "D", "d", "", "8", "s", "p", "*", "|", "_", "h", "H", 0, 4, "<", "3",
1, 5, ">", "4", 2, 6, "^", "2", 3, 7, "v", "1", "None", None, " ", ""]
descripts = ["point", "plus", "pixel", "cross", "circle", "diamond", "thin diamond", "",
"octagon", "square", "pentagon", "star", "vertical bar", "horizontal bar", "hexagon 1", "hexagon 2",
"tick left", "caret left", "triangle left", "tri left", "tick right", "caret right", "triangle right", "tri right",
"tick up", "caret up", "triangle up", "tri up", "tick down", "caret down", "triangle down", "tri down",
"Nothing", "Nothing", "Nothing", "Nothing"]
fig, ax = plt.subplots(1, 1, figsize=(14, 4))
for x, y, m, d in zip(xs.T.flat, ys.T.flat, markers, descripts):
ax.scatter(x, y, marker=m, s=100)
ax.text(x + 0.1, y - 0.1, d, size=14)
ax.set_axis_off()
plt.show()
In [ ]:
%load exercises/3.2-markers.py
In [ ]:
t = np.arange(0.0, 5.0, 0.2)
plt.plot(t, t, , t, t**2, , t, t**3, )
plt.show()
Line styles are about as commonly used as colors. There are a few predefined linestyles available to use. Note that there are some advanced techniques to specify some custom line styles. Here is an example of a custom dash pattern.
linestyle | description |
---|---|
'-' | solid |
'--' | dashed |
'-.' | dashdot |
':' | dotted |
'None' | draw nothing |
' ' | draw nothing |
'' | draw nothing |
Also, don't mix up ".-" (line with dot markers) and "-." (dash-dot line) when using the plot
function!
In [ ]:
t = np.arange(0.0, 5.0, 0.2)
plt.plot(t, t, '-', t, t**2, '--', t, t**3, '-.', t, -t, ':')
plt.show()
It is a bit confusing, but the line styles mentioned above are only valid for lines. Whenever you are dealing with the linestyles of the edges of "Patch" objects, you will need to use words instead of the symbols. So "solid" instead of "-", and "dashdot" instead of "-.". This issue will be fixed for the v2.1 release and allow these specifications to be used interchangably.
In [ ]:
fig, ax = plt.subplots(1, 1)
ax.bar([1, 2, 3, 4], [10, 20, 15, 13], ls='dashed', ec='r', lw=5)
plt.show()
With just about any plot you can make, there are many attributes that can be modified to make the lines and markers suit your needs. Note that for many plotting functions, matplotlib will cycle the colors for each dataset you plot. However, you are free to explicitly state which colors you want used for which plots. For the plt.plot()
and plt.scatter()
functions, you can mix the specification for the colors, linestyles, and markers in a single string.
In [ ]:
t = np.arange(0., 5., 0.2)
# red dashes, blue squares and green triangles
plt.plot(t, t, 'r--', t, t**2, 'bs', t, t**3, 'g^')
plt.show()
Property | Value Type |
---|---|
alpha | float |
color or c | any matplotlib color |
dash_capstyle | ['butt', 'round' 'projecting'] |
dash_joinstyle | ['miter' 'round' 'bevel'] |
dashes | sequence of on/off ink in points |
drawstyle | [ ‘default’ ‘steps’ ‘steps-pre’ |
‘steps-mid’ ‘steps-post’ ] | |
linestyle or ls | [ '-' '--' '-.' ':' 'None' ' ' ''] |
and any drawstyle in combination with a | |
linestyle, e.g. 'steps--'. | |
linewidth or lw | float value in points |
marker | [ 0 1 2 3 4 5 6 7 'o' 'd' 'D' 'h' 'H' |
'' 'None' ' ' None '8' 'p' ',' |
|
'+' 'x' '.' 's' '*' '_' '|' | |
'1' '2' '3' '4' 'v' '<' '>' '^' ] | |
markeredgecolor or mec | any matplotlib color |
markeredgewidth or mew | float value in points |
markerfacecolor or mfc | any matplotlib color |
markersize or ms | float |
solid_capstyle | ['butt' 'round' 'projecting'] |
solid_joinstyle | ['miter' 'round' 'bevel'] |
visible | [True False ] |
zorder | any number |
Make a plot that has a dotted red line, with large yellow diamond markers that have a green edge
In [ ]:
%load exercises/3.3-properties.py
In [ ]:
t = np.arange(0.0, 5.0, 0.1)
a = np.exp(-t) * np.cos(2*np.pi*t)
plt.plot(t, a, )
plt.show()
Another very important property of many figures is the colormap. The job of a colormap is to relate a scalar value to a color. In addition to the regular portion of the colormap, an "over", "under" and "bad" color can be optionally defined as well. NaNs will trigger the "bad" part of the colormap.
As we all know, we create figures in order to convey information visually to our readers. There is much care and consideration that have gone into the design of these colormaps. Your choice in which colormap to use depends on what you are displaying. In mpl, the "jet" colormap has historically been used by default, but it will often not be the colormap you would want to use. Much discussion has taken place on the mailing lists with regards to what colormap should be default. The upcoming v2.0 release of matplotlib will take on a new default colormap along with some other stylistic changes to the defaults. The defaults will be discussed this week during SciPy 2015.
I want to acknowedge Nicolas Rougier and Tony Yu for putting significant effort in educating users in proper colormap selections. Here is the full gallery of all the pre-defined colormaps, organized by the types of data they are usually used for.
In [ ]:
%load http://matplotlib.org/mpl_examples/color/colormaps_reference.py # For those with v1.2 or higher
In [ ]:
"""
Reference for colormaps included with Matplotlib.
This reference example shows all colormaps included with Matplotlib. Note that
any colormap listed here can be reversed by appending "_r" (e.g., "pink_r").
These colormaps are divided into the following categories:
Sequential:
These colormaps are approximately monochromatic colormaps varying smoothly
between two color tones---usually from low saturation (e.g. white) to high
saturation (e.g. a bright blue). Sequential colormaps are ideal for
representing most scientific data since they show a clear progression from
low-to-high values.
Diverging:
These colormaps have a median value (usually light in color) and vary
smoothly to two different color tones at high and low values. Diverging
colormaps are ideal when your data has a median value that is significant
(e.g. 0, such that positive and negative values are represented by
different colors of the colormap).
Qualitative:
These colormaps vary rapidly in color. Qualitative colormaps are useful for
choosing a set of discrete colors. For example::
color_list = plt.cm.Set3(np.linspace(0, 1, 12))
gives a list of RGB colors that are good for plotting a series of lines on
a dark background.
Miscellaneous:
Colormaps that don't fit into the categories above.
"""
import numpy as np
import matplotlib.pyplot as plt
cmaps = [('Sequential', ['binary', 'Blues', 'BuGn', 'BuPu', 'gist_yarg',
'GnBu', 'Greens', 'Greys', 'Oranges', 'OrRd',
'PuBu', 'PuBuGn', 'PuRd', 'Purples', 'RdPu',
'Reds', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd']),
('Sequential (2)', ['afmhot', 'autumn', 'bone', 'cool', 'copper',
'gist_gray', 'gist_heat', 'gray', 'hot', 'pink',
'spring', 'summer', 'winter']),
('Diverging', ['BrBG', 'bwr', 'coolwarm', 'PiYG', 'PRGn', 'PuOr',
'RdBu', 'RdGy', 'RdYlBu', 'RdYlGn', 'seismic']),
('Qualitative', ['Accent', 'Dark2', 'hsv', 'Paired', 'Pastel1',
'Pastel2', 'Set1', 'Set2', 'Set3', 'spectral']),
('Miscellaneous', ['gist_earth', 'gist_ncar', 'gist_rainbow',
'gist_stern', 'jet', 'brg', 'CMRmap', 'cubehelix',
'gnuplot', 'gnuplot2', 'ocean', 'rainbow',
'terrain', 'flag', 'prism'])]
nrows = max(len(cmap_list) for cmap_category, cmap_list in cmaps)
gradient = np.linspace(0, 1, 256)
gradient = np.vstack((gradient, gradient))
def plot_color_gradients(cmap_category, cmap_list):
fig, axes = plt.subplots(nrows=nrows)
fig.subplots_adjust(top=0.95, bottom=0.01, left=0.2, right=0.99)
axes[0].set_title(cmap_category + ' colormaps', fontsize=14)
for ax, name in zip(axes, cmap_list):
ax.imshow(gradient, aspect='auto', cmap=plt.get_cmap(name))
pos = list(ax.get_position().bounds)
x_text = pos[0] - 0.01
y_text = pos[1] + pos[3]/2.
fig.text(x_text, y_text, name, va='center', ha='right', fontsize=10)
# Turn off *all* ticks & spines, not just the ones with colormaps.
for ax in axes:
ax.set_axis_off()
for cmap_category, cmap_list in cmaps:
plot_color_gradients(cmap_category, cmap_list)
plt.show()
When colormaps are created in mpl, they get "registered" with a name. This allows one to specify a colormap to use by name.
In [ ]:
fig, (ax1, ax2) = plt.subplots(1, 2)
z = np.random.random((10, 10))
ax1.imshow(z, interpolation='none', cmap='gray')
ax2.imshow(z, interpolation='none', cmap='coolwarm')
plt.show()
Oftentimes, you just simply need that superscript or some other math text in your labels. Matplotlib provides a very easy way to do this for those familiar with LaTeX. Any text that is surrounded by dollar signs will be treated as "mathtext". Do note that because backslashes are prevelent in LaTeX, it is often a good idea to prepend an r
to your string literal so that Python will not treat the backslashes as escape characters.
In [ ]:
plt.scatter([1, 2, 3, 4], [4, 3, 2, 1])
plt.title(r'$\sigma_i=15$', fontsize=20)
plt.show()
There are two ways one can place arbitrary text anywhere they want on a plot. The first is a simple text()
. Then there is the fancier annotate()
function that can help "point out" what you want to annotate.
In [ ]:
t = np.arange(0.0, 5.0, 0.01)
s = np.cos(2*np.pi*t)
plt.plot(t, s, lw=2)
plt.annotate('local max', xy=(2, 1), xytext=(3, 1.5),
arrowprops=dict(facecolor='black', shrink=0.05))
plt.ylim(-2, 2)
plt.show()
There are all sorts of boxes for your text, and arrows you can use, and there are many different ways to connect the text to the point that you want to annotate. For a complete tutorial on this topic, go to the Annotation Guide. In the meantime, here is a table of the kinds of arrows that can be drawn
In [ ]:
import matplotlib.patches as mpatches
styles = mpatches.ArrowStyle.get_styles()
ncol = 2
nrow = (len(styles)+1) // ncol
figheight = (nrow+0.5)
fig = plt.figure(figsize=(4.0*ncol/0.85, figheight/0.85))
fontsize = 0.4 * 70
ax = fig.add_axes([0, 0, 1, 1])
ax.set_xlim(0, 4*ncol)
ax.set_ylim(0, figheight)
def to_texstring(s):
s = s.replace("<", r"$<$")
s = s.replace(">", r"$>$")
s = s.replace("|", r"$|$")
return s
for i, (stylename, styleclass) in enumerate(sorted(styles.items())):
x = 3.2 + (i//nrow)*4
y = (figheight - 0.7 - i%nrow)
p = mpatches.Circle((x, y), 0.2, fc="w")
ax.add_patch(p)
ax.annotate(to_texstring(stylename), (x, y),
(x-1.2, y),
ha="right", va="center",
size=fontsize,
arrowprops=dict(arrowstyle=stylename,
patchB=p,
shrinkA=50,
shrinkB=5,
fc="w", ec="k",
connectionstyle="arc3,rad=-0.25",
),
bbox=dict(boxstyle="square", fc="w"))
ax.set_axis_off()
plt.show()
In [ ]:
%load exercises/3.4-arrows.py
In [ ]:
t = np.arange(0.0, 5.0, 0.01)
s = np.cos(2*np.pi*t)
plt.plot(t, s, lw=2)
plt.annotate('local max', xy=(2, 1), xytext=(3, 1.5),
arrowprops=dict())
plt.ylim(-2, 2)
plt.show()
A Patch object can have a hatching defined for it.
Letters can be combined, in which case all the specified hatchings are done. If same letter repeats, it increases the density of hatching of that pattern.
In [ ]:
bars = plt.bar([1, 2, 3, 4], [10, 12, 15, 17])
plt.setp(bars[0], hatch='x', facecolor='w')
plt.setp(bars[1], hatch='xx-', facecolor='orange')
plt.setp(bars[2], hatch='+O.', facecolor='c')
plt.setp(bars[3], hatch='*', facecolor='y')
plt.show()
The topic of transforms in matplotlib, that is the ability to map the coordinates specified by your data to the coordinates of your figure, is very advanced and will not be covered in this tutorial. For those who are interested in learning about them, see the transformation tutorial. For those who are really daring, there are the developer guides to transforms and scales. While most users will never, ever need to understand matplotlib transforms to the level described in those links, it is important to be aware of them, and their critical role in figure-making.
In a figure, there are four coordinate systems: display, figure, axes, and data. Transforms are used to convert coordinates in one system into another system for various uses. This is how matplotlib knows exactly where to place the ticks and ticklabels, even when you change the axis limits. The ticker says that the tick and label "1.5", for example, are to go at data x-coordinate 1.5. The transform says that location is at 0.4 in axes x-coordinate space. Meanwhile, the xlabel of "Distance" is placed at axes x-coordinate space of 0.5 (half-way). Meanwhile, a legend might be placed at a location relative to the figure coordinates.
Furthermore, the transform system is what is used to allow various scales to work, such as log scales. The transform system is what is used to make the polar plots work seamlessly. Whether you realize it or not, you use the transforms system in matplotlib all the time. Everything drawn in matplotlib has a transform associated with it. Most of the time, you will never notice this, and will happily operate within the data coordinate system. But when you want to do some more advanced plots, with some eye-catching visual tricks, the transform system will be there for you.
Matplotlib's greatest strength is its ability to give you complete control over every single aspect of your plots and figures. Matplotlib's second greatest strength is its ability to take as much control over as much of your plots and figures as you want. You, as the user, would never consider to use matplotlib if you had to specify all of these things for every single plot. Most of the time, the defaults are exactly what you want them to be.
Matplotlib uses the matplotlibrc configuration file to define the plethora of defaults found in matplotlib. You can control the defaults of almost every property in matplotlib: figure size and dpi, line width, color and style, axes, axis and grid properties, text and font properties and so on. Just modify your rc file and re-run your scripts to produce your improved figures.
To display where the currently active matplotlibrc file was loaded from, one can do the following:
In [ ]:
import matplotlib
print(matplotlib.matplotlib_fname())
You can also change the rc settings during runtime within a python script or interactively from the python shell. All of the rc settings are stored in a dictionary-like variable called matplotlib.rcParams
, which is global to the matplotlib package. rcParams
can be modified directly. Newer versions of matplotlib can use rc()
, for example:
In [ ]:
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rcdefaults() # for when re-running this cell
fig, (ax1, ax2) = plt.subplots(1, 2)
ax1.plot([1, 2, 3, 4])
mpl.rc('lines', linewidth=2, linestyle='-.')
# Equivalent older, but still valid syntax
#mpl.rcParams['lines.linewidth'] = 2
#mpl.rcParams['lines.linestyle'] = '-.'
ax2.plot([1, 2, 3, 4])
plt.show()
To learn more, please see this guide on customizing matplotlib.